
package trim.testalgorithms;

import java.util.*;
import trim.common.*;
import trim.differentmaterials.core.*;
import trim.differentmaterials.generator.core.*;
import trim.differentmaterials.generator.columns.*;
import trim.differentmaterials.coefficients.*;

/**
 *
 * @author Podorov Alexander
 */

public class ExpRestTrimmer2 extends RestTrimmer
{
    private int totalRests;
    private int maxMaterials;
    private int currentMaterials;

    private Material material;

    private final int[] formatLengths;

    private boolean isEnd;
    private boolean isUnuseable;
    private boolean isBacktrack;

    private final List<int[]> deque;
    private int recursionDepth;

    private Coefficients coefficients;

    public ExpRestTrimmer2(TrimCore core, Generator generator, Coefficients coefficients)
    {
        super(core, generator);

        formatLengths = new int[formatCount];
        for (int i = 0; i < formatCount; i++)
        {
            formatLengths[i] = formatOrder[i].getFormat().getLength();
        }

        deque = new ArrayList<int[]>();

        this.coefficients = coefficients;
    }    

    public void restsToPatterns()
    {
        if (isEnd)
        {
            recursionDepth--;
            return;
        }
        /*if (isBacktrack)
        {
            if (recursionDepth != 0)
            {
                return;
            }
        }*/

        isUnuseable = false;
        isBacktrack = false;

        int tail = material.getLength();
        int knifes = material.getKnifeCount();
        int count = 0;

        int[] items = new int[formatCount];
        int[] bestItems = new int[formatCount];

        int[] indexes = new int[formatCount];

        int dimension = formatCount;

        int[] temp = new int[formatCount];

        for (int i = 0; i < formatCount; i++)
        {
            if (formatRests[i] == 0)
            {
                temp[i] = - 1;
                dimension--;
            }
            else
            {
                temp[i] = i;
            }
        }

        int next = 0;

        indexes = new int[dimension];

        for (int i = 0; i < formatCount; i++)
        {
            if (temp[i] != - 1)
            {
                indexes[next] = i;
                next++;
            }
        }

        if (dimension == 1)
        {
            bestItems[indexes[0]] = Math.min(tail / formatLengths[indexes[0]], formatRests[indexes[0]]);
        }
        else
        {
            int current = - 1;

            boolean exit = false;

            while (!exit)
            {
                while (true)
                {
                    if (current == dimension - 1)
                    {
                        break;
                    }
                    else
                    {
                        current++;
                    }
                    if ((formatLengths[indexes[current]] <= tail) && (items[indexes[current]] == 0))
                    {
                        items[indexes[current]] = Math.min(tail / formatLengths[indexes[current]], formatRests[indexes[current]]);

                        tail -= items[indexes[current]] * formatLengths[indexes[current]];
                        count += items[indexes[current]];

                        if ((totalRests + tail <= currentMaterials) && (count <= knifes))
                        {
                            for (int i = 0; i < dimension; i++)
                            {
                                bestItems[indexes[i]] = items[indexes[i]];
                            }

                            isUnuseable = true;
                            
                            currentMaterials -= material.getLength();

                            deque.add(bestItems);

                            for (int i = 0; i < formatCount; i++)
                            {
                                formatRests[i] -= bestItems[i];
                                totalRests -= bestItems[i] * formatLengths[i];
                            }

                            if (!isFormatRests())
                            {
                                isEnd = true;
                                recursionDepth--;
                                return;
                            }

                            recursionDepth++;
                            restsToPatterns();
                        }
                    }
                }

                while (true)
                {
                    current--;

                    if (items[indexes[current]] > 0)
                    {
                        items[indexes[current]]--;
                        tail += formatLengths[indexes[current]];
                        count--;

                        break;
                    }
                    else
                    {
                        if (current == 0)
                        {
                            exit = true;

                            break;
                        }
                    }
                }
            }
        }

        if (!isUnuseable)
        {
            if (deque.size() > 0)
            {
                int[] removed = deque.remove(deque.size() - 1);            
                for (int i = 0; i < formatCount; i++)
                {
                    formatRests[i] += removed[i];                
                    totalRests += removed[i] * formatLengths[i];

                }
                currentMaterials += material.getLength();

                recursionDepth--;

                //isBacktrack = true;

                return;
            }
            else
            {
                currentMaterials += material.getLength();

                recursionDepth++;
                restsToPatterns();
            }


            

            
        }
    }

    /*int current = 0;
        int lastFormat = 0;
        int[] list = new int[dimension];
        int last = - 1;

        while ((current != dimension + 1) && (record != length))
        {
            for (int j = current; j < dimension; j++)
            {
                items[indexes[j]] = Math.min(tail / formats[indexes[j]], formatRests[indexes[j]]);

                if (items[indexes[j]] > 0)
                {
                    tail -= items[indexes[j]] * formats[indexes[j]];
                    value += items[indexes[j]] * formats[indexes[j]];
                    count += items[indexes[j]];

                    if ((record < value) && (count <= knifes))
                    {
                        record = value;
                        for (int i = 0; i < dimension; i++)
                        {
                            bestItems[indexes[i]] = items[indexes[i]];
                        }
                    }

                    list[++last] = j;
                }
            }


            if (last >= 0)
            {
                lastFormat = list[last];
                if (items[indexes[lastFormat]] == 1)
                {
                    last--;
                }

                items[indexes[lastFormat]]--;
                tail += formats[indexes[lastFormat]];
                value -= formats[indexes[lastFormat]];
                count--;
                current = lastFormat + 1;
            }
            else
            {
                current = dimension + 1;
            }

        }*/

    public void start() throws NoSolutionsException
    {
        setFormatRests();

        isEnd = false;

        material = materialStock[0].getMaterial();

        totalRests = 0;
        for (int i = 0; i < formatCount; i++)
        {
            totalRests += formatOrder[i].getFormat().getLength() * formatRests[i];
        }

        if (totalRests % material.getLength() == 0)
        {            
            maxMaterials = totalRests / material.getLength();
        }
        else
        {            
            maxMaterials = totalRests / material.getLength() + 1;
        }        

        maxMaterials *= material.getLength();
        currentMaterials = maxMaterials;

        recursionDepth = 0;
        isBacktrack = false;

        restsToPatterns();        

        for (int[] array : deque)
        {
            RealPattern pat = new RealPattern(array, 0.0, coefficients.getCoefficient(RealPattern.class), material);
            pat.setCount(1);
            patterns.add(pat);
        }        
    }
}
